One path for multiple input/output
This page documents emitter behavior and customization when you use union operator |
or @sharedRoute
to express multiple input/output for a given path.
Default behaviors​
The simplest way to express a combination of input in TypeSpec is to use the union operator |
.
At a glance, JS and Python supports natively union, while Java and C# will use overloads.
- TypeSpec
- Python
- CSharp
- Typescript
- Java
@service({
title: "Analyze",
version: "v1",
})
namespace Analyze;
@route("/analyze")
@post
op analyze(@query mode: "strict" | "lenient", @body image: bytes): AnalyzeResult;
model CompletionInput {
input: string | string[];
}
@route("/completions")
@post
op completions(@body input: CompletionInput): CompletionResult;
def analyze(image: bytes, *, mode: Literal["strict", "lenient"]) -> AnalyzeResult:
...
class CompletionInput:
input: Union[str, List[str]] = rest_field(readonly=True)
def completions(input: CompletionInput) -> CompletionResult:
...
// Union "strict" | "lenient" will be generate as extensible enum
namespace Analyze.Models
{
public readonly partial struct Mode : IEquatable<Mode>
{
public static Mode Strict { get; } = new Mode(StrictValue);
public static Mode Lenient { get; } = new Mode(LenientValue);
}
}
// other union which cannot be convert to enum will generate as BinaryData
namespace Analyze.Models
{
public partial class CompletionInput
{
public CompletionInput(BinaryData input)
public BinaryData Input { get; }
}
}
namespace Analyze
{
public partial class AnalyzeClient
{
//protocol method
public virtual async Task<Response> AnalyzeAsync(string mode, RequestContent content, RequestContext context = null) {}
public virtual Response Analyze(string mode, RequestContent content, RequestContext context = null) {}
//convenience method
public virtual async Task<Response<AnalyzeResult>> AnalyzeAsync(Mode mode, BinaryData image, CancellationToken cancellationToken = default) {}
public virtual Response<AnalyzeResult> Analyze(Mode mode, BinaryData image, CancellationToken cancellationToken = default) {}
//protocol method
public virtual async Task<Response> CompletionsAsync(RequestContent content, RequestContext context = null) {}
public virtual Response Completions(RequestContent content, RequestContext context = null) {}
//convenience method
public virtual async Task<Response<CompletionResult>> CompletionsAsync(CompletionInput input, CancellationToken cancellationToken = default) {}
public virtual Response<CompletionResult> Completions(CompletionInput input, CancellationToken cancellationToken = default) {}
}
}
// from user experience perspective
export interface CompletionInput {
input: string | string[];
}
export type DemoServiceContext = Client & {
path: {
/** Resource for '/analyze' has methods for the following verbs: post */
(path: "/analyze"): {
post(
options: {
body: string;
queryParameters: {
mode: "strict" | "lenient";
};
} & RequestParameters,
): StreamableMethod<Analyze200Response | AnalyzeDefaultResponse>;
};
/** Resource for '/completions' has methods for the following verbs: post */
(path: "/completions"): {
post(
options: {
body: CompletionInput;
} & RequestParameters,
): StreamableMethod<Completions200Response | CompletionsDefaultResponse>;
};
};
};
public enum Mode {
STRICT("strict"),
LENIENT("lenient");
}
public final class CompletionInput {
public CompletionInput(BinaryData input)
public BinaryData getInput()
}
public final class AnalyzeClient {
public Response<BinaryData> analyzeWithResponse(String mode, BinaryData image, RequestOptions requestOptions)
public Response<BinaryData> completionsWithResponse(BinaryData input, RequestOptions requestOptions)
public AnalyzeResult analyze(Mode mode, byte[] image)
public CompletionResult completions(CompletionInput input)
}
Using union implies that the entire combination of possible input is valid. If you have a specific set of combination, or connection between input and output,
you must use @sharedRoute
. By default, codegen will generate one method per operation name.
- TypeSpec
- Python
- CSharp
- Typescript
- Java
@sharedRoute
@route("/foo")
op a(x: int32): float;
@sharedRoute
@route("/foo")
op b(x: string): int64;
def a(x: int) -> float:
# code
def b(x: string) -> int:
# code
//protocol
public Response A(RequestContent content, RequestContext context);
public Response B(RequestContent content, RequestContext context);
//convenience
public Response<float> A(int x, CancellationToken token);
public Response<long> B(string x, Cancellation token);
// from user experience perspective
export type DemoServiceContext = Client & {
path: {
/** Resource for '/foo' has methods for the following verbs: post */
(path: "/foo"): {
post(
options?: {
body?: {
x: number;
};
} & RequestParameters,
): StreamableMethod<A200Response>;
post(
options?: {
body?: {
x: string;
};
} & RequestParameters,
): StreamableMethod<B200Response>;
};
};
};
public final class Client {
public Response<BinaryData> aWithResponse(BinaryData request, RequestOptions requestOptions)
public Response<BinaryData> bWithResponse(BinaryData request, RequestOptions requestOptions)
public double a(int x)
public long b(String x)
}
Customizations​
Merge @sharedRoute
operations into one.​
If your shared routes are actually one unique semantic operation, you may want to configure codegen to use a unique name. This is simply done by renaming both operations to the same name using @clientName
- TypeSpec
- Python
- CSharp
- Typescript
- Java
// main.tsp
@sharedRoute
@route("/foo")
op a(x: int) : float
@sharedRoute
@route("/foo")
op b(x: string) : int64
// client.tsp
import "./main.tsp";
import "@azure-tools/typespec-client-generator-core";
using Azure.ClientGenerator.Core;
@@clientName(a, "Foo");
@@clientName(b, "Foo");
@overload
def foo(x: int) -> float:
...
@overload
def foo(x: string) -> int:
...
def foo(x: string | int) -> float | int:
# Code here
//protocol
public Response Foo(RequestContent content, RequestContext context);
//convenience
public Response<float> Foo(int x, CancellationToken token);
public Response<long> Foo(string x, Cancellation token);
JS RLC is not in the business of customization with client.tsp
NOT_SUPPORTED